﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Echegoyen.Models;
using System.Web.Security;
using WebMatrix.WebData;

namespace Echegoyen.Controllers
{

    public class MaterialController : Controller
    {
        private IContext db;
        private IWebSecurity ws;
        private IMyLogger log;

        public MaterialController(IContext db, IWebSecurity ws, IMyLogger log)
        {
            this.db = db;
            this.ws = ws;
            this.log = log;
        }

        //
        // GET: /Material/

        public ActionResult Index() 
        {
            if (TempData["result"] == null)
                return View(db.Material.ToList());
            else
            {
                TempData["msg"] = "Mostrando resultados de la búsqueda.";
                return View(TempData["result"]);
            }
        }

        // GET
        public ActionResult Buscar()
        {
            List<SelectListItem> lTM = db.TipoMaterial.ToList().Select(x => new SelectListItem
            {
                Value = x.TipoMaterialId.ToString(),
                Text = x.Tipo
            }).ToList();
            lTM.Add(new SelectListItem { Value = "-1", Text = "Cualquiera", Selected = true });
            ViewBag.TiposMaterial = lTM;

            List<SelectListItem> lE = db.Editorial.ToList().Select(x => new SelectListItem
            {
                Value = x.EditorialId.ToString(),
                Text = x.Nombre
            }).ToList();
            lE.Add(new SelectListItem { Value = "-1", Text = "Cualquiera", Selected = true });
            ViewBag.Editoriales = lE;

            List<SelectListItem> lT = db.Tema.ToList().Select(x => new SelectListItem
            {
                Value = x.TemaId.ToString(),
                Text = x.Nombre
            }).ToList();
            lT.Add(new SelectListItem { Value = "-1", Text = "Cualquiera", Selected = true });
            ViewBag.Temas = lT;

            List<SelectListItem> lA = db.Autor.ToList().Select(x => new SelectListItem
            {
                Value = x.AutorId.ToString(),
                Text = x.Nombre + " " + x.Apellido
            }).ToList();
            lA.Add(new SelectListItem { Value = "-1", Text = "Cualquiera", Selected = true });
            ViewBag.Autores = lA;

            return View();
        }

        // POST
        [HttpPost, ActionName("Buscar")]
        [ValidateAntiForgeryToken]
        public ActionResult BuscarResult(Material criterio)
        {
            IEnumerable<Material> result = db.Material.ToList();

            if (criterio.AutoresId.ToList()[0] != -1)  // -1 significa "cualquiera"
            {
                Autor autorToFind = db.Autor.Find(criterio.AutoresId.ToList()[0]);
                result = result.Where(m => m.Autores.Any(a => a.Equals(autorToFind))).ToList(); // .ToList() para que el IQueryable ejecute ahora mismo (ver http://geekswithblogs.net/DarrenFieldhouse/archive/2011/12/12/objectdisposedexception-in-entity-framework.aspx)
            }
            if (criterio.TemaId.ToList()[0] != -1)
            {
                Tema temaToFind = db.Tema.Find(criterio.TemaId.ToList()[0]);
                result = result.Where(m => m.Tema.ToList().Contains(temaToFind)).ToList();
            }
            if (criterio.EditorialId != -1)
                result = result.Where( m => m.EditorialId == criterio.EditorialId );
            if (criterio.TipoMaterialId != -1)
                result = result.Where(m => m.TipoMaterialId == criterio.TipoMaterialId).ToList();
            if (criterio.Titulo != null && !criterio.Titulo.Equals(""))
                result = result.Where(m => m.Titulo.ToLower().Contains(criterio.Titulo.ToLower())).ToList();

            TempData["result"] = result;
                
            return RedirectToAction("Index");
        }


        // GET
        [Authorize(Roles = "Admins, Clientes")]
        public ActionResult Reservar(int id = 0)
        {       
            string uid = ws.CurrentUserName;
            Usuario u = db.Usuario.Where(x => x.NombreUsuario == uid).ToList()[0];

            int cuantasInact = db.Reserva.Where(x => x.UsuarioId == u.UsuarioId && x.Estado == "Pendiente").ToList().Count;
            int cuantasAct = db.Prestamo.Where(x => x.UsuarioId == u.UsuarioId && x.Estado == "Asignado").ToList().Count;
            if (cuantasInact + cuantasAct >= 5)
            {
                TempData["msg"] = "Alcanzaste el máximo de reservas (5).";
                return RedirectToAction("Index");
            }

            return View();
        }

        // POST
        [Authorize(Roles = "Admins, Clientes")]
        [HttpPost, ActionName("Reservar")]
        [ValidateAntiForgeryToken]
        public ActionResult ReservarConfirmed(int id = 0)
        {   
            Material material = db.Material.Find(id);
            if (material == null)
            {
                log.Error("MaterialController", "No se puede reservar un material inexistente: " + id, null);         
                return HttpNotFound();
            }

            string uid = ws.CurrentUserName;
            Usuario u = db.Usuario.Where(x => x.NombreUsuario == uid).ToList()[0];
            Ejemplar ej = material.ObtenerDisponible();

            if (ej == null) // Reserva
            {
                db.Reserva.Add(new Reserva(0, u, material, DateTime.Now));
                db.SaveChanges();

                TempData["msg"] = "Reserva efectuada. Se esperará un ejemplar disponible.";
                return RedirectToAction("Index", "Reserva");
            }
            else // Préstamo asignado
            {
                db.Prestamo.Add(new Prestamo(0, u, db.Ejemplar.Find(ej.EjemplarId), DateTime.Now));
                db.SaveChanges();

                TempData["msg"] = "Reserva efectuada y ejemplar asignado. Retirar antes de los 3 días.";
                return RedirectToAction("Index", "Reserva");
            }
        }


        //
        // GET: /Material/Details/5

        public ActionResult Details(int id = 0)
        {
            Material material = db.Material.Find(id);
            if (material == null)
            {
                log.Error("MaterialController", "No se pueden dar los detalles de un material inexistente: " + id, null);             
                return HttpNotFound();
            }

            ViewBag.Reservas = db.Reserva.Where(x => x.MaterialId == material.MaterialId && x.Estado.Equals("Pendiente")).ToList();
            ViewBag.Prestamos = db.Prestamo.Where(x => x.Ejemplar.MaterialId == material.MaterialId).OrderBy(x => x.Fecha).ToList();

            return View(material);
        }

        //
        // GET: /Material/Create

        [Authorize(Roles = "Admins")]
        public ActionResult Create()
        {
            ViewBag.TiposMaterial = db.TipoMaterial.ToList().Select(x => new SelectListItem
            {
                Value = x.TipoMaterialId.ToString(),
                Text = x.Tipo
            });

            ViewBag.Editoriales = db.Editorial.ToList().Select(x => new SelectListItem
            {
                Value = x.EditorialId.ToString(),
                Text = x.Nombre
            });

            ViewBag.Temas = db.Tema.ToList().Select(x => new SelectListItem
            {
                Value = x.TemaId.ToString(),
                Text = x.Nombre
            });

            ViewBag.Autores = db.Autor.ToList().Select(x => new SelectListItem
            {
                Value = x.AutorId.ToString(),
                Text = x.Nombre + " " + x.Apellido
            });

            return View();
        }

        //
        // POST: /Material/Create
        [Authorize(Roles = "Admins")]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Material material)
        {  
            if (ModelState.IsValid)
            {
                material.Autores = new List<Autor>();
                if(material.AutoresId != null)
                    foreach (int i in material.AutoresId)
                        material.Autores.Add(db.Autor.Find(i));
                material.Tema = new List<Tema>();
                if (material.TemaId != null)
                    foreach (int i in material.TemaId)
                        material.Tema.Add(db.Tema.Find(i));
                db.Material.Add(material);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            log.Error("MaterialController", "El material que se pretendía crear no es válido.", null);        
            return View(material);
        }

        //
        // GET: /Material/Edit/5
        [Authorize(Roles = "Admins")]
        public ActionResult Edit(int id = 0)
        {
            Material material = db.Material.Find(id);
            if (material == null)
            {
                log.Error("MaterialController", "No se puede editar un material inexistente: " + id, null);               
                return HttpNotFound();
            }

            ViewBag.TiposMaterial = db.TipoMaterial.ToList().Select(x => new SelectListItem
            {
                Value = x.TipoMaterialId.ToString(),
                Text = x.Tipo
            });

            ViewBag.Editoriales = db.Editorial.ToList().Select(x => new SelectListItem
            {
                Value = x.EditorialId.ToString(),
                Text = x.Nombre
            });

            ViewBag.Temas = db.Tema.ToList().Select(x => new SelectListItem
            {
                Value = x.TemaId.ToString(),
                Text = x.Nombre
            });

            ViewBag.Autores = db.Autor.ToList().Select(x => new SelectListItem
            {
                Value = x.AutorId.ToString(),
                Text = x.Nombre + " " + x.Apellido
            });

            return View(material);
        }

        //
        // POST: /Material/Edit/5
        [Authorize(Roles = "Admins")]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Material material)
        {
            // Esto es para que se guarden las relaciones many-to-many a pesar del lazy loading. Ref.: http://stackoverflow.com/questions/9520111/entity-framework-4-not-saving-my-many-to-many-rows
            db.Entry(material).State = EntityState.Modified;
            db.Entry(material).Collection(m => m.Autores).Load();
            db.Entry(material).Collection(m => m.Tema).Load();

            if (ModelState.IsValid)
            {              
                material.Autores = new List<Autor>();
                if (material.AutoresId != null)
                foreach (int i in material.AutoresId)            
                    material.Autores.Add(db.Autor.Find(i));
               
                material.Tema = new List<Tema>();
                if (material.TemaId != null)
                foreach (int i in material.TemaId)
                    material.Tema.Add(db.Tema.Find(i));

                db.SaveChanges();
                return RedirectToAction("Index");
            }

            log.Error("MaterialController", "El material que se pretendía editar no es válido.", null);     
            return View(material);
        }

        //
        // GET: /Material/Delete/5
        [Authorize(Roles = "Admins")]
        public ActionResult Delete(int id = 0)
        {
            Material material = db.Material.Find(id);
            if (material == null)
            {
                log.Error("MaterialController", "No se puede eliminar un material inexistente: " + id, null);    
                return HttpNotFound();
            }
            return View(material);
        }

        //
        // POST: /Material/Delete/5
        [Authorize(Roles = "Admins")]
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Material material = db.Material.Find(id);
            db.Material.Remove(material);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}